﻿'Copyright (c) 2009 Grailtek

'Permission is hereby granted, free of charge, to any person
'obtaining a copy of this software and associated documentation
'files (the "Software"), to deal in the Software without
'restriction, including without limitation the rights to use,
'copy, modify, merge, publish, distribute, sublicense, and/or sell
'copies of the Software, and to permit persons to whom the
'Software is furnished to do so, subject to the following
'conditions:

'The above copyright notice and this permission notice shall be
'included in all copies or substantial portions of the Software.

'THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
'EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
'OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
'NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
'HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
'WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
'FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
'OTHER DEALINGS IN THE SOFTWARE.

Imports System.Reflection
Imports System.Data.Common
Imports System.Data.SqlClient
Imports System.ExecutorServices
Imports System.DataExecutorServices.Exceptions
Imports System.Aspect

<AttributeUsage(AttributeTargets.Delegate)> _
Public MustInherit Class BulkSqlDataCallAttribute
    Inherits SqlDataCallAttribute

    Public ReadOnly TableName As String
    Public ReadOnly Timeout As Integer

    Public Sub New(ByVal iUIName As String, ByVal iTableName As String, ByVal iTimeout As Integer, ByVal iStoredProcedureName As String, ByVal iConnectionString As String)
        MyBase.New(iUIName, iStoredProcedureName, GetDatabaseFromConnectionString(iConnectionString), iConnectionString)
        TableName = iTableName
        Timeout = iTimeout
    End Sub

    Public Sub New(ByVal iUIName As String, ByVal iTableName As String, ByVal iTimeout As Integer, ByVal iStoredProcedureName As String, ByVal iDatabase As String, ByVal iConnectionString As String)
        MyBase.New(iUIName, iStoredProcedureName, iDatabase, iConnectionString)
        TableName = iTableName
        Timeout = iTimeout
    End Sub

    Private Sub DoBulkInsert(ByVal args() As Argument)
        Try

            If args.Count <> 1 Then
                Throw New BulkSqlDataCallException
            End If

            Dim arg As Argument = args.First
            If Not arg.Param.ParameterType.IsArray Then
                Throw New BulkSqlDataCallException
            End If

            Dim ElementType As Type = arg.Param.ParameterType.GetElementType
            Dim Array As Array = DirectCast(arg.Value, Array)

            Dim mappings As OutputMapping() = OutputMapping.GetMappings(ElementType)

            Using conn As SqlConnection = GetSqlConnection()
                conn.Open()
                Using cmd As New SqlCommand("DELETE FROM " & TableName, conn)
                    cmd.ExecuteNonQuery()
                End Using
                Using da As New SqlDataAdapter(String.Format("SELECT * FROM {0} WHERE 1=0", TableName), conn)
                    Using tbl As New DataTable()
                        da.Fill(tbl)
                        For Each item As Object In Array
                            Dim row As DataRow = tbl.NewRow()
                            For Each mapping As OutputMapping In mappings
                                Dim o As Object = mapping.GetValue(item)
                                If o Is Nothing Then o = DBNull.Value 'NOTE: this is apparently required for bulk inserts with NULL values
                                row(mapping.FieldAttribute.RegisterName) = o
                            Next
                            tbl.Rows.Add(row)
                        Next
                        Using blk As New SqlBulkCopy(conn, SqlBulkCopyOptions.Default, Nothing)
                            blk.BulkCopyTimeout = Timeout
                            blk.DestinationTableName = TableName
                            blk.WriteToServer(tbl, DataRowState.Added)
                            blk.Close()
                        End Using
                    End Using
                End Using
                conn.Close()
                conn.Dispose()
            End Using
        Catch sqlEx As SqlException
            HandleSqlEx(sqlEx)
        End Try
    End Sub

    Protected Overrides Sub Execute(ByVal args() As Argument)
        DoBulkInsert(args)
        MyBase.Execute(New Argument() {})
    End Sub

    Protected Overrides Function ExecuteReturnTable(ByVal args() As Argument) As DataTable
        DoBulkInsert(args)
        Return MyBase.ExecuteReturnTable(New Argument() {})
    End Function

End Class